﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using System.Configuration;
using devtm.Helpers;
using System.Collections;
using System.Reflection;
using System.ComponentModel;
using devtm.Configuration;
using devtm.Collections;
using System.Globalization;
using devtm.Configuration.Models;
using devtm.Editor.Configuration.Templates;
using devtm.Editor.Configuration.Messages;
using devtm.editor.Configuration;
using System.Diagnostics;
using System.Xml.Linq;
using System.Text;


namespace devtm.Editor.Configuration
{


    public class ElementSection : TreeViewItem, IElementSection
    {

        public const string ExcludeType = "|webRequestModules|system.windows.forms|uri|system.diagnostics|authenticationModules|connectionManagement|protocols|httpModules|webControls|deviceFilters|partialTrustVisibleAssemblies|urlMappings|clientTarget|webParts|sqlCacheDependency|outputCacheSettings|defaultProxy|mobileControls|customErrors|healthMonitoring|pages|system.serviceModel.activation|compilation|webServices|LockAllAttributesExcept|LockElements|LockAllElementsExcept|LockAllAttributesExcept|LockAttributes|system.web.extensions|system.serviceModel|CurrentConfiguration|ElementInformation|BindingCollectionElement|SectionInformation|EndpointCollectionElement|CustomBindingElementCollection|";
        
        #region ctor

        public ElementSection(ElementSection parentItem, System.Configuration.ConfigurationSectionCollection sections, string name, int recurcif)
        {

            ButtonActions = new Collections.TableCollection<IElementAction, string>(c => c.ActionName);
            
            if (recurcif == 0)
            {
                Ignore = true;
                return;
            }
            recurcif--;


            this.Header = new TitleTreeview() { Context = new ContainerHeader(false) { Label = name, ImageSource = "root.png" } };
            this.ToolTip = name;
            this.ParentItem = parentItem;

            foreach (String key in sections.Keys)
            {
                
                if (ExcludeType.Contains("|" + key + "|"))
                    continue;

                
                try
                {
                    Datas.Lazy.Resolver.LastResolve.Clear();
                    var section = Datas.Lazy.GetObject(() => sections[key]);

                    if (!Datas.ShowNode(section))
                        continue;

                    Debug.Print(key);

                    ElementSection masection = new ElementSection(this, section, key, recurcif);

                    if (masection.Ignore || masection.ConfigurationElement == null)
                        continue;

                    masection.ViewAddItemCollection = Visibility.Collapsed;
                    this.Items.Add(masection);

                }
                catch (Exception ex)
                {
                    Datas.NotifyError(key, ex);
                }
                              
            }

        }


        public ElementSection(ElementSection parentItem, ConfigurationElement section, string name, int recurcif)
        {

            ButtonActions = new Collections.TableCollection<IElementAction, string>(c => c.ActionName);

            if (recurcif == 0 || !Datas.ShowNode(section))
            {
                Ignore = true;
                return;
            }
            recurcif--;

            this.ParentItem = parentItem;
            this.ConfigurationElement = section;

            var l = (this.ConfigurationElement as System.Configuration.ConfigurationSection);
            if (l != null)
                this.SectionInformation = l.SectionInformation;

            name = OverrideName(name);

            if (ExcludeType.Contains("|" + name + "|"))
            {
                Ignore = true;
                return;
            }


            if (ConfigurationElement is ConfigurationSection)
                if (ConfigurationElement.ElementInformation.IsPresent)
                {
                    SectionInformation cc = (ConfigurationElement as ConfigurationSection).SectionInformation;
                    this.RawXml = XElement.Parse(cc.GetRawXml());
                    this.ToolTip = cc.GetRawXml();
                }
        

            this.SetHeader(name, name, ConfigurationElement.GetType(), !ConfigurationElement.ElementInformation.IsPresent);
            this.ToolTip = name;

            Build(name, recurcif);

            if (HasCollectionType.ToString().EndsWith("Collection"))
                ButtonFactory.FindButtonCollection(this);

            ButtonFactory.ButtonClass(this, ButtonActions);

            TestErrors(); 
        }

        public void Build(string name, int recurcif)
        {

            
            this.Items.Clear();

            #region childrens
            
            if (this.ConfigurationElement != null)
            {

                Type tpe = this.ConfigurationElement.GetType();

                if (tpe.IsOfType(typeof(IEnumerable)))
                    // on charge ses enfants.
                    LoadItemSectionCollection2(ConfigurationElement.GetType().Name, recurcif);
                else
                    // tourne sur les proprietes
                    LoadChildren(recurcif);

            }

            #endregion

        }

        public void TestErrors()
        {

            ContextTitle.SectionInError = System.Windows.Visibility.Hidden;

            if (ConfigurationElement != null)
            {

                if (ConfigurationElement.ElementInformation.Errors.Count >= 1)
                {
                    ContextTitle.SectionInError = System.Windows.Visibility.Visible;


                    foreach (ConfigurationException item in ConfigurationElement.ElementInformation.Errors)
                    {

                        //System.Configuration.ConfiguationErrors
                        MessageTextError msg = new MessageTextError(
                            Datas.Messages,
                            (item as Exception).Message,
                            TypeMessage.Error)
                        {
                            Label = Properties.Resources.Close,
                            Line = item.Line
                        };


                        msg.Action = () => msg.List.Remove(msg);

                        Datas.Messages.Add(msg);
                    }


                }
                
            }

        }

        //public void TraceNewObject(ConfigurationElement section)
        //{
        //    Datas.Lazy.ExecuteObject(
        //        () => section.GetType().CreateTemplateCode(@"devtm.Editor.Configuration.Customs", @"d:\test\", false, Path)
        //        );
        //}

        public ElementSection(ElementSection parentItem, ConfigurationSectionGroup sectiongroup, string name, int recurcif)
        {

            ButtonActions = new Collections.TableCollection<IElementAction, string>(c => c.ActionName);

            if (recurcif == 0 || "scripting".Contains(name))
            {
                Ignore = true;
                return;
            }
            recurcif--;

            this.ParentItem = parentItem;
            this.SetHeader(name, name, null, !sectiongroup.IsDeclared);
            this.ToolTip = name;
            HasGroup = true;
            Group = sectiongroup;

            LoadChildren(sectiongroup, recurcif);

        }

        private void LoadChildren(int recurcif)
        {

                foreach (PropertyInfo prop in this.ConfigurationElement.GetType().GetProperties(~BindingFlags.Static | ~BindingFlags.NonPublic))
                {

                    if (ExcludeType.Contains("|" + prop.Name + "|"))
                        continue;

                    if (!prop.PropertyType.IsOfType(typeof(ConfigurationElement)))
                        continue;

                    var attrs = prop.GetCustomAttributes(true);
                    if (attrs.Length == 0)
                        continue;

                    if (prop.GetIndexParameters().Count() > 0)
                    {
                        // on est dans un tableau indexé
                        int countmax = 100;
                        foreach (PropertyInfo prop1 in ConfigurationElement.GetType().GetProperties(~BindingFlags.Static | BindingFlags.Public))
                        {
                            if (prop1.Name.ToLower() == "count")
                            {
                                countmax = (int)prop1.GetValue(ConfigurationElement, null);
                                if (countmax > 0)
                                    LoadItemSectionCollection(prop, recurcif, countmax--);
                            }

                        }

                    }
                    else
                        LoadItemSectionProperties(prop, recurcif);

                } // for


        }

        private void LoadChildren(ConfigurationSectionGroup sectiongroup, int recurcif)
        {

            if (!Datas.ShowNode(sectiongroup))
                return;

            foreach (String key in sectiongroup.SectionGroups.Keys)
            {
                Debug.Print(key);

                ConfigurationSectionGroup group = sectiongroup.SectionGroups[key];
                
                if (!Datas.ShowNode(group))
                    return;

                ElementSection s = new ElementSection(this, group, key, recurcif);
                if (s.Ignore)
                    continue;
                this.Items.Add(s);
            }

            foreach (String key in sectiongroup.Sections.Keys)
            {

                Debug.Print(key);

                if (ExcludeType.Contains("|" + key + "|"))
                    continue;

                try
                {
                    Datas.Lazy.Resolver.LastResolve.Clear();
                    ConfigurationElement c = Datas.Lazy.GetObject(() => sectiongroup.Sections[key]);

                    if (!Datas.ShowNode(c))
                        return;

                    ElementSection s = new ElementSection(this, c, key, recurcif);

                    if (s.Ignore || s.ConfigurationElement == null)
                        continue;

                    s.ViewAddItemCollection = Visibility.Collapsed;

                    this.Items.Add(s);

                }
                catch (Exception ex)
                {
                    Datas.NotifyError(key, ex);
                    //TODO: voir pour les plantage de lecture de valeur

                }

            }

        }

        private void LoadItemSectionProperties(PropertyInfo prop, int recurcif)
        {

            ConfigurationElement c;
            string name = prop.ResolveConfigurationName();
            try
            {
                Datas.Lazy.Resolver.LastResolve.Clear();
                c = Datas.Lazy.GetObject(() => (ConfigurationElement)prop.GetValue(ConfigurationElement, null));

                if (c == ConfigurationElement)
                    return;

                ElementSection s = new ElementSection(this, c, name, recurcif);
                s.ContextTitle.Inherited = this.ContextTitle.Inherited;
                this.Items.Add(s);

            }
            catch (Exception ex)
            {

                Datas.NotifyError(prop, ex);

            }
            

        }

        private void LoadItemSectionCollection(PropertyInfo prop, int recurcif, int countmax)
        {

            for (int i = 0; i < countmax; i++)
            {
                ConfigurationElement j;
                try
                {
                    j = (ConfigurationElement)prop.GetValue(ConfigurationElement, new object[] { i });
                }
                catch (Exception ex)
                {
                    Datas.NotifyError(prop, ex);
                    return;
                }
                string name = prop.ResolveName();
                AddItemFromCollection(name, recurcif, j);
                DetermineTypeCollection();
            }

        }

        private void LoadItemSectionCollection2(string name, int recurcif)
        {
            IEnumerable cc = (IEnumerable)ConfigurationElement;
            ContextTitle.ImageSource = "List.png";
            DetermineTypeCollection();
            foreach (ConfigurationElement j in cc)
                AddItemFromCollection(name, recurcif, j);
        }

        internal IElementSection AddItemFromCollection(string name, int recurcif, ConfigurationElement j)
        {

            name = SearchName(name, j);

            ElementSection k = new ElementSection(this, j, name, recurcif);
            k.CanDel = true;

            string img = ((this.ParentItem as ElementSection).Header as UserControl).DataContext.Property("Label", "");
            if (string.IsNullOrEmpty(img))
                img = name;
            NameIsBind = true;
            k.ContextTitle.ImageSource = GetImage(img, j.GetType());
            k.ContextTitle.Inherited = System.Windows.Visibility.Hidden;
            this.Items.Add(k);
            return k;

        }

        private static string SearchName(string name, ConfigurationElement j)
        {

            Type type = j.GetType();
            PropertyInfo p = null;

            if (type.IsOfType(typeof(KeyValueConfigurationElement)))
            {
                if (j.PropertyTryGet("key", out p))
                    if (p.PropertyType == typeof(string))
                        name = (string)p.GetValue(j, null);
            }
            else
            {
                if (j.PropertyTryGet("name", out p))
                    if (p.PropertyType == typeof(string))
                        name = (string)p.GetValue(j, null);
            }

            if (string.IsNullOrEmpty(name))
                if (j.PropertyTryGet("type", out p))
                    if (p.PropertyType == typeof(string))
                        name = (string)p.GetValue(j, null);
            return name;
        }

        private string OverrideName(string name)
        {

            if (this.ConfigurationElement == null)
                return name;

            Type t = this.ConfigurationElement.GetType();
            t = devtm.Editor.Configuration.Templates.DictionaryTemplate.Get(t, this);
            if (t != null)
            {
                string O = t.DisplayName();
                if (!string.IsNullOrEmpty(O))
                    return O;
            }
            return name;
        }

        #region Header

        private string GetImage(string name, Type type)
        {
            if (Datas.Images.ContainsKey(name))
                return (string)(Datas.Images[name]);

            return "dossier.png";
        }


        public void SetName(string name)
        {
            if (ContextTitle != null)
            {
                ContextTitle.Label = name;
                this.ToolTip = name;
            }
        }


        public void SetHeader(string name, string image, Type type, bool inherited)
        {
            ContextTitle = new ContainerHeader(inherited) { Label = name, ImageSource = GetImage(image, type) };
            Header = new TitleTreeview() { Context = ContextTitle };
            this.ToolTip = name;
        }



        #endregion

        private void DetermineTypeCollection()
        {

            if (this.ConfigurationElement is ConnectionStringSettingsCollection)
                HasCollectionType = EnumTypeConfiguration.ConnectionStringCollection;

            else if (this.ConfigurationElement is ProviderSettingsCollection)
            {
                #region ProviderSettings

                HasCollectionType = EnumTypeConfiguration.ProviderSettingsCollection;
                //ButtonActions.Add(
                //    new ElementAction(
                //        EnumAction.Add,
                //        "Add3",
                //        Properties.Resources.AddAItem,
                //        () => ActionsHelper.AddItemToSectionProviderSettings(this),
                //        this.ConfigurationElement,
                //        this)
                //    );

                #endregion
            }

            else if (this.ConfigurationElement is KeyValueConfigurationCollection)
                HasCollectionType = EnumTypeConfiguration.KeyValueCollection;

            else
                if (ConfigurationElement.GetType().IsOfType(typeof(ConfigurationElementCollection)))
                {
                    #region typecollection

                    switch ((this.ConfigurationElement as ConfigurationElementCollection).CollectionType)
                    {
                        case ConfigurationElementCollectionType.AddRemoveClearMap:
                            HasCollectionType = EnumTypeConfiguration.AddRemoveClearMapCollection;
                            break;
                        case ConfigurationElementCollectionType.AddRemoveClearMapAlternate:
                            HasCollectionType = EnumTypeConfiguration.AddRemoveClearMapAlternateCollection;
                            break;
                        case ConfigurationElementCollectionType.BasicMap:
                            HasCollectionType = EnumTypeConfiguration.BasicMapCollection;
                            break;
                        case ConfigurationElementCollectionType.BasicMapAlternate:
                            HasCollectionType = EnumTypeConfiguration.BasicMapAlternateCollection;
                            break;
                        default:
                            HasCollectionType = EnumTypeConfiguration.UndefineCollection;
                            break;
                    }

                    #endregion

                    HasCollectionType = EnumTypeConfiguration.ElementCollection;
                    IsComplexCollection = true;

                }
                else
                    HasCollectionType = EnumTypeConfiguration.UndefineCollection;

        }

        #endregion

        public Visibility ViewAddItemCollection { get; set; }

        public bool Ignore { get; set; }

        public ConfigurationElement ConfigurationElement { get; set; }

        public bool HasGroup { get; set; }

        public ConfigurationSectionGroup Group { get; set; }

        public EnumTypeConfiguration HasCollectionType { get; set; }

        private bool? _candel = null;
        public bool CanDel 
        { 
            get
            {

                if (_candel != null)
                    return (bool)_candel;

                if (ConfigurationElement != null)
                {
                    if (ConfigurationElement.GetType().IsOfType(typeof(ConfigurationSection)))
                    {

                        if (ConfigurationElement.GetType().IsOfType(typeof(ConnectionStringsSection), typeof(AppSettingsSection)))
                            _candel = false;
                        
                        if (ConfigurationElement.ElementInformation.IsLocked || !ConfigurationElement.ElementInformation.IsPresent)
                            _candel = false;

                        if (_candel == null)
                            _candel = true;

                    }
                    else if (ConfigurationElement.GetType().IsOfType(typeof(ConfigurationElement)))
                    {
                        if (this.HasCollectionType.ToString().EndsWith("Collection"))
                            _candel = false;

                        if (ConfigurationElement.ElementInformation.IsLocked || !ConfigurationElement.ElementInformation.IsPresent)
                            _candel = false;

                        if (ConfigurationElement.GetType().IsOfType(typeof(ConnectionStringSettings), typeof(KeyValueConfigurationElement)))
                            _candel = true;

                        if (_candel == null)
                            _candel = true;

                    }
                }

                if (_candel == null)
                    _candel = false;

                return (bool)_candel;
            } 
            set
            {
                _candel = value;
            }
        }

        public IElementSection ParentItem { get; protected set; }
        
        public string Path
        {
            get
            {
                return ParentItem != null && (this.Header is TitleTreeview)
                    ? ParentItem.Path + @"\" + (this.Header as TitleTreeview).Context.Label.ToString() 
                    : string.Empty;
            }
        }


        public int Level
        {
            get
            {
                int ret = ParentItem != null
                    ? ParentItem.Level
                    : 0;

                return ret + 1;
            }
        }


        public IElementSection AddConfigurationElement(ConfigurationElement configElement, string name)
        {


            if (this.HasCollectionType.ToString().EndsWith("Collection"))
            {
                return AddItemFromCollection(name, 5, configElement);
            }
            else
            {
                ElementSection section = new ElementSection(this, configElement, name, 10);
                if (section.Ignore || section.ConfigurationElement == null)
                    return null;

                section.ViewAddItemCollection = Visibility.Collapsed;
                this.Items.Add(section);
                return section;
            }

        }

        private bool NameIsBind = false;
        public void ConfigProperty_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {

            if (NameIsBind || e.PropertyName != "Name")
                return;

            string i = (string)(sender as IConfigProperty).Value;
            TitleTreeview t1 = (TitleTreeview)Header;
            t1.Context.Label = i;

        }


        /// <summary>
        /// Determines whether this contains the specified element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns>
        /// 	<c>true</c> if [contains] [the specified element]; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(ConfigurationElement element)
        {
            bool test = false;
            foreach (ElementSection item2 in Items)
            {
                if (item2.ConfigurationElement == element)
                {
                    test = true;
                    break;
                }
            }
            return test;
        }

        public SectionInformation SectionInformation { get; set; }

        public IEnumerable<IConfigProperty> Children { get; set; }

        public TableCollection<IElementAction, string> ButtonActions { get; set; }

        public IContainerHeader ContextTitle { get; protected set; }

        public bool IsComplexCollection { get; set; }


        public string GetElementName
        {
            get 
            {
                if (!(ParentItem.ConfigurationElement is ConfigurationElementCollection))
                    throw new Exception("Can't call GetElementName if it is not a collection context.");

                var oo = AccessPrivatePropertyHelper.GetMethod(ParentItem.ConfigurationElement, "get_ElementName").ToList();

                return (string) oo.FirstOrDefault().Invoke(ParentItem.ConfigurationElement, null);

            }
        }


        public ConfigurationSection Section
        {
            get
            {
                if (ConfigurationElement == null)
                    return null;

                if (ConfigurationElement is ConfigurationSection)
                    return ConfigurationElement as ConfigurationSection;

                return ParentItem.Section;

            }
           
        }

        public XElement RawXml { get; set; }

        public void SetRawXml(string configurationSection)
        {

            ConfigurationSection section = ConfigurationElement as ConfigurationSection;
            section.SectionInformation.SetRawXml(configurationSection);

            Build(Name, 10);

        }


        public const char spliter = '|';
        public string CastToClipboard()
        {
            ConfigurationSection section = ConfigurationElement as ConfigurationSection;
            StringBuilder s = new StringBuilder();

            s.Append(section.SectionInformation.Name);
            s.Append(spliter);

            s.Append(section.GetType().AssemblyQualifiedName);
            s.Append(spliter);

            s.Append(Section.SectionInformation.IsProtected ? Section.SectionInformation.ProtectionProvider.Name : string.Empty);
            s.Append(spliter);

            s.Append(Section.SectionInformation.ConfigSource ?? string.Empty);
            s.Append(spliter);

            s.Append(section.SectionInformation.GetRawXml());

            return s.ToString();

        }

    }
}
